import React from 'react';
import Typography from "@material-ui/core/Typography/Typography";
import TextField from "@material-ui/core/TextField/TextField";
import withStyles from "@material-ui/core/styles/withStyles";
import MenuItem from "@material-ui/core/MenuItem/MenuItem";
import Button from "@material-ui/core/Button/Button";
import {connect} from 'react-redux';
import CircularProgress from "@material-ui/core/CircularProgress/CircularProgress";
import {green} from "@material-ui/core/colors";
import {displayError, displayInfo} from "./util/Message";
import apiUrls from './util/apiUrls';
import InputAdornment from "@material-ui/core/InputAdornment/InputAdornment";
import HelpIcon from "@material-ui/icons/HelpOutline";
import Tooltip from "@material-ui/core/Tooltip/Tooltip";
import ListItemText from "@material-ui/core/ListItemText/ListItemText";


const styles = theme => ({
    textField: {
        marginLeft: theme.spacing.unit,
        marginRight: theme.spacing.unit,
        width: 160
    },
    textFieldFocused: {
        '& $helpIcon': {
            display: 'flex',
        }
    },
    submitButton: {
        marginTop: 2 * theme.spacing.unit,
        marginLeft: '50%',
        transform: 'translateX(-50%)',
        letterSpacing: '0',
    },
    buttonProgress: {
        color: green[500],
        position: 'absolute',
        top: '50%',
        left: '50%',
        marginTop: -12,
        marginLeft: -12,
    },
    helpIcon: {
        color: 'rgba(139,139,139,0.5)',
        display: 'none',
    }
});


class OperationParamForm extends React.Component {
    constructor(props) {
        super(props);
        const params = this.getParamsFromOperation();
        // 这里的pNames直接作为属性存储在组件对象上，而不是state中，因为它们不变，所以不放在state中
        this.pNames = this.getPNames();
        this.selectValues = this.getSelectValues();
        this.state = params;
    }

    getSelectValues() {
        return this.props.operation.params.filter(param => param.type === 'select').reduce((pre, now) => {
            pre[now.name] = now.value;
            return pre;
        }, {});
    }

    getParamsFromOperation() {
        return this.props.operation.params.reduce((pre, now) => {
            switch (now.type) {
                case 'input': {
                    pre[now.name] = {
                        value: '',
                        changed: false,
                        error: false,
                        errorText: '',
                    };
                    return pre;
                }
                case 'select': {
                    pre[now.name] = {
                        value: 0,
                        isSelect: true,
                    };
                    return pre;
                }
                default: {
                    return pre;
                }
            }
        }, {});
    }

    getPNames() {
        return this.props.operation.params.reduce((pre, now) => {
            pre[now.name] = now.pName;
            return pre;
        }, {})
    }

    translateLimitToCH(limit) {
        if (limit) {
            return limit.split(/\s/).map(limit => {
                if (limit === 'odd') {
                    return '奇数';
                } else if (limit.startsWith('>=')) {
                    const number = limit.substr(2);
                    return `大于等于${number}`;
                } else if (limit.startsWith('>')) {
                    const number = limit.substr(1);
                    return `大于${number}`;
                } else if (limit.startsWith('<=')) {
                    const number = limit.substr(2);
                    return `小于等于${number}`;
                } else if (limit.startsWith('<')) {
                    const number = limit.substr(1);
                    return `小于${number}`;
                } else if (limit === 'int') {
                    return '整数';
                }
            }).join(' ');
        }
        return '';
    }


    handleChange = (name, limit, event) => {
        let {value, type} = event.target;
        const [hasError, errorText] = this.checkErrors(type, value, limit);
        if (type === 'text') {
            this.setState({
                ...this.state,
                [name]: {
                    value,
                    changed: true,
                    error: hasError,
                    errorText,
                }
            })
        } else {
            this.setState({
                ...this.state,
                [name]: {
                    ...this.state[name],
                    value
                }
            })
        }
    };

    checkErrors(type, value, limit) {
        let error = false;
        let errorText = '';
        if (type === 'text') {
            if (isNaN(value)) {
                error = true;
                errorText = '请输入正确的数字';
            } else if (value === '') {
                error = true;
                errorText = '请输入值';
            } else if (limit) {
                limit.split(' ').forEach(limit => {
                    if (limit === 'odd' && !(value & 1)) {
                        error = true;
                        errorText = '请输入奇数';
                    }
                    if (limit.startsWith('>')) {
                        if (limit.startsWith('>=')) {
                            const number = +limit.substr(2);
                            if (value < number) {
                                error = true;
                                errorText = `请输入大于等于${number}的数`;
                            }
                        } else {
                            const number = +limit.substr(1);
                            if (value <= number) {
                                error = true;
                                errorText = `请输入大于${number}的数`;
                            }
                        }
                    }
                    if (limit.startsWith('<')) {
                        if (limit.startsWith('<=')) {
                            const number = +limit.substr(2);
                            if (value > number) {
                                error = true;
                                errorText = `请输入小于等于${number}的数`;
                            }
                        } else {
                            const number = +limit.substr(1);
                            if (value >= number) {
                                error = true;
                                errorText = `请输入小于${number}的数`;
                            }
                        }
                    }
                    if (limit === 'int' && parseInt(value) !== +value) {
                        error = true;
                        errorText = '请输入整数';
                    }
                });
            }
        }
        return [error, errorText];
    }

    handleSubmit = () => {
        const {code, name} = this.props.operation;
        const {image, process} = this.props;
        const params = {};
        const paramsCH = {};
        for (let [key, value] of Object.entries(this.state)) {
            paramsCH[key] = value.isSelect ? this.selectValues[key][value.value] : value.value;
            params[this.pNames[key]] = value.value;
        }
        if (process.length) {
            this.addProcessAndInitState(code, name, params, paramsCH, process[process.length - 1].processedImage);
        } else {
            const fileReader = new FileReader();
            fileReader.onload = e => {
                this.addProcessAndInitState(code, name, params, paramsCH, e.target.result.split(',')[1]);
            };
            fileReader.readAsDataURL(image);
        }
    };

    addProcessAndInitState(code, name, params, paramsCH, image) {
        this.props.addProcess(code, name, params, paramsCH, image);
        const initParams = this.getParamsFromOperation();
        this.setState({
            ...initParams
        })
    }

    render() {
        const {operation, classes, isPending} = this.props;
        const params = this.state;
        const hasError = Object.values(params).some(value => value.error === undefined ? false : value.error);
        const allChanged = Object.values(params).every(value => value.changed === undefined ? true : value.changed);
        return (
            <div>
                <Typography align='center' variant='h6'>参数区</Typography>
                <form autoComplete='off'>
                    {operation.params.map(param => {
                        let helperText = this.translateLimitToCH(param.limit);
                        if (params[param.name].error && params[param.name].errorText) {
                            helperText = params[param.name].errorText;
                        }
                        return (
                            <React.Fragment key={param.name}>
                                {param.type === 'input' ? (
                                    <TextField id={param.name} className={classes.textField} label={param.name}
                                               value={params[param.name].value} helperText={helperText && helperText}
                                               InputProps={param.desc && ({
                                                   endAdornment: (
                                                       <InputAdornment position="end" className={classes.helpIcon}>
                                                           <Tooltip title={param.desc}>
                                                               <HelpIcon/>
                                                           </Tooltip>
                                                       </InputAdornment>
                                                   ),
                                                   classes: {
                                                       focused: classes.textFieldFocused,
                                                   }
                                               })}
                                               margin='dense'
                                               required
                                               error={params[param.name].error}
                                               onChange={(e) => {
                                                   this.handleChange(param.name, param.limit, e);
                                               }}/>
                                ) : (
                                    <TextField id={param.name} className={classes.textField} label={param.name}
                                               select fullWidth required
                                               value={params[param.name].value} margin='dense'
                                               onChange={(e) => {
                                                   this.handleChange(param.name, param.limit, e);
                                               }}>
                                        {
                                            param.value.map((item, index) => {
                                                return (
                                                    <MenuItem key={item} value={index}>
                                                        {
                                                            param.valueDesc[index] ? (
                                                                <Tooltip title={param.valueDesc[index]}>
                                                                    <ListItemText primary={item}/>
                                                                </Tooltip>) : <ListItemText primary={item}/>
                                                        }
                                                    </MenuItem>
                                                )
                                            })
                                        }
                                    </TextField>
                                )}
                            </React.Fragment>
                        )
                    })}
                    <Button variant='contained' color='primary' disabled={!allChanged || hasError || isPending}
                            className={classes.submitButton} onClick={this.handleSubmit}>添加操作</Button>
                    {isPending && <CircularProgress size={60} className={classes.buttonProgress}/>}
                </form>
            </div>
        )
    }
}

const mapDispatchToProps = dispatch => {
    return {
        addProcess(code, name, params, paramsCH, image) {
            dispatch(dispatch => {
                dispatch({
                    type: 'ADD_PENDING'
                });
                const postBody = {
                    operations: [{
                        code,
                        params,
                    }],
                    image,
                };
                fetch(apiUrls.sendOperation, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify(postBody)
                }).then(data => data.json()).then(res => {
                    if (res.result) {
                        if (res.message.log) {
                            dispatch({
                                type: 'ADD_PROCESS',
                                name,
                                code,
                                params: paramsCH,
                                image,
                                processedImage: res.message.image,
                                log: res.message.log,
                            });
                            displayInfo(dispatch, res.message.log.str, false);
                        } else {
                            dispatch({
                                type: 'ADD_PROCESS',
                                name,
                                code,
                                params: paramsCH,
                                image,
                                processedImage: res.message.image
                            });
                        }
                    } else {
                        displayError(dispatch, res.message);
                    }
                }).catch(() => {
                    displayError(dispatch, '出错啦，请重试！');
                }).finally(() => {
                    dispatch({
                        type: 'DES_PENDING'
                    })
                });

            })
        }
    }
};

const mapStateToProps = state => {
    return {
        image: state.image.image,
        isPending: state.pending > 0,
        process: state.process.present,
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(OperationParamForm));